#include "core.hpp"

#include <cstdlib>

namespace canyon{

void VM::cmd_exit(command){
    std::exit(1);
}

/* x from 0 to 3 */
void VM::cmd_mov_irx(command cmd, size_t x){
    this->regs.set_16(reg::IVAL, x, cmd.config.data.data16);
}

void VM::cmd_mov_i0m(command cmd){
    this->memory.set(
        this->regs.get_full(cmd.config.regs.reg2),
        cmd.config.regs.reg1
    );
}

void VM::cmd_mov_r0r0(command cmd){
    this->regs.set(
        cmd.config.regs.reg2, 0,
        this->regs.get(cmd.config.regs.reg1, 0)
    );
}

void VM::cmd_mov_rr(command cmd){
    this->regs.set_full(
        cmd.config.regs.reg2,
        this->regs.get_full(cmd.config.regs.reg1)
    );
}

/* x from 0 to 7 */
void VM::cmd_mov_rxm(command cmd, size_t x){
    this->memory.set(
        this->regs.get_full(cmd.config.regs.reg2),
        this->regs.get(cmd.config.regs.reg1, x)
    );
}

void VM::cmd_mov_rm(command cmd){
    this->memory.set_64(
        this->regs.get_full(cmd.config.regs.reg2),
        this->regs.get_full(cmd.config.regs.reg1)
    );
}

/* x from 0 to 7 */
void VM::cmd_mov_mrx(command cmd, size_t x){
    this->regs.set(
        cmd.config.regs.reg2, x,
        this->memory.get(this->regs.get_full(cmd.config.regs.reg1))
    );
}

void VM::cmd_mov_mm(command cmd){
    this->memory.set(
        this->regs.get_full(cmd.config.regs.reg2),
        this->memory.get(this->regs.get_full(cmd.config.regs.reg1))
    );
}

void VM::cmd_swap_rr(command cmd){
    uint64_t temp = this->regs.get_full(cmd.config.regs.reg1);
    this->regs.set_full(cmd.config.regs.reg1, this->regs.get_full(cmd.config.regs.reg2));
    this->regs.set_full(cmd.config.regs.reg2, temp);
}

void VM::cmd_swap_mm(command cmd){
    uint8_t temp = this->memory.get(this->regs.get_full(cmd.config.regs.reg1));
    this->memory.set(
        this->regs.get_full(cmd.config.regs.reg1),
        this->memory.get(this->regs.get_full(cmd.config.regs.reg2))
    );
    this->memory.set(
        this->regs.get_full(cmd.config.regs.reg2),
        temp
    );
}

/* x from 0 to 7 */
void VM::cmd_swap_r_0x(command cmd, size_t x){
    uint8_t temp = this->regs.get(cmd.config.regs.reg1, x);
    this->regs.set(cmd.config.regs.reg1, x, this->regs.get(cmd.config.regs.reg2, x));
    this->regs.set(cmd.config.regs.reg2, x, temp);
}

void VM::cmd_band(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) & this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_bor(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) | this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_bxor(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) ^ this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_bnot(command cmd){
    this->regs.set_full(
        cmd.config.regs.reg2,
        ~this->regs.get_full(cmd.config.regs.reg1)
    );
}

void VM::cmd_bleft(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) << this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_bright(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) >> this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_add(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) + this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_sub(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) - this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_mul(command cmd){
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) * this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_div(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg2) == 0) {
        // 处理除零错误，当前临时不作处理
        return;
    }
    this->regs.set_full(
        reg::ALU,
        this->regs.get_full(cmd.config.regs.reg1) / this->regs.get_full(cmd.config.regs.reg2)
    );
}

void VM::cmd_cmp(command cmd){
    /*
    // 比较结果可以存储在ALU寄存器中
    uint64_t a = this->regs.get_full(cmd.config.regs.reg1);
    uint64_t b = this->regs.get_full(cmd.config.regs.reg2);
    this->regs.set_full(reg::ALU, (a == b) ? 0 : (a > b) ? 1 : -1);
    */
}

void VM::cmd_jmp(command cmd){
    this->set_codeadr(this->regs.get_full(cmd.config.regs.reg1));
}

void VM::cmd_jmp_a_if_b(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg2) != 0) {
        this->set_codeadr(this->regs.get_full(cmd.config.regs.reg1));
    }
}

void VM::cmd_jmp_a_ifn_b(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg2) == 0) {
        this->set_codeadr(this->regs.get_full(cmd.config.regs.reg1));
    }
}

void VM::cmd_jmp_adr_ife(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg1) == this->regs.get_full(cmd.config.regs.reg2)) {
        this->set_codeadr(this->regs.get_full(reg::ADR));
    }
}

void VM::cmd_jmp_adr_ifne(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg1) != this->regs.get_full(cmd.config.regs.reg2)) {
        this->set_codeadr(this->regs.get_full(reg::ADR));
    }
}

void VM::cmd_jmp_adr_ifalb(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg1) < this->regs.get_full(cmd.config.regs.reg2)) {
        this->set_codeadr(this->regs.get_full(reg::ADR));
    }
}

void VM::cmd_jmp_adr_ifagb(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg1) > this->regs.get_full(cmd.config.regs.reg2)) {
        this->set_codeadr(this->regs.get_full(reg::ADR));
    }
}

void VM::cmd_jmp_adr_ifaleb(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg1) <= this->regs.get_full(cmd.config.regs.reg2)) {
        this->set_codeadr(this->regs.get_full(reg::ADR));
    }
}

void VM::cmd_jmp_adr_ifageb(command cmd){
    if (this->regs.get_full(cmd.config.regs.reg1) >= this->regs.get_full(cmd.config.regs.reg2)) {
        this->set_codeadr(this->regs.get_full(reg::ADR));
    }
}

void VM::cmd_map_dm(command cmd){
    this->bus.load(
        this->regs.get_full(reg::DEV),
        Memory(
            this->memory,
            this->regs.get_full(cmd.config.regs.reg2),
            this->regs.get_full(cmd.config.regs.reg1)
        )
    );
}

void VM::cmd_umap_d(command cmd){
    this->bus.unload(this->regs.get_full(cmd.config.regs.reg1));
}

void VM::cmd_info_dev_count(command){
    this->regs.set_full(reg::RET, this->bus.size());
}

void VM::cmd_call_d(command cmd){
    this->bus.call(
        this->regs.get_full(reg::DEV),
        cmd.config.regs.reg1,
        this->regs.get_full(cmd.config.regs.reg2)
    );
}

}
